#include "Task.hpp"

// 完成进程池
void Debug1(vector<channel>& channels)
{
    for(int i = 0;i < NUM;i++)
    {
        cout<<"PID:"<<channels[i]._processid<<"---name:"<<channels[i]._processname<<"---cmdfd"<<channels[i]._cmdfd<<endl;
    }
}

void slaver()
{
    while(1)
    {
        int cmdnum = 0;
        int n = read(0,&cmdnum,sizeof(cmdnum));// 0 已经被子进程的读端覆盖了
        if(n == 0)break;

        tasks[cmdnum]();
        //cout<<"my PID:"<<getpid()<<" Obtain PID:"<<cmdnum<<endl;
    }
}

void InitProcessPool(vector<channel>& channels)
{
    vector<int> oldfd;// 记录父进程的历史写端
    for(int i = 1;i <= NUM;i++)
    {
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);// 检查

        int id = fork();
        if(id == 0)// child
        {
            for(auto& t:oldfd)// 关闭子进程继承父进程的写端
            {
                close(t);
            }

            close(pipefd[1]);// 子进程关闭写端
            dup2(pipefd[0],0);// 让子进程从0上拿数据 ----> 设计：让子进程没有管道的概念
            //slaver(pipefd[0]);// 接受任务码并完成相应的任务
            slaver();
            close(pipefd[0]);// 任务完成后子进程关闭读端
            exit(0);
        }

        // father
        close(pipefd[0]);// 父进程关闭读端
        string str = "process" + to_string(i);
        channels.push_back(channel(pipefd[1],str,id));// 对结构体成员初始化并组织起来
        oldfd.push_back(pipefd[1]);
        // 父进程的写端也就是 channel里的cmdfd 要等清理退出时才会去关闭
    }
}

// 菜单
void menu()
{
    cout<<"-------------------------"<<endl;
    cout<<"|   1.利物浦  2.阿森纳   |"<<endl;
    cout<<"|   3.切尔西  4.布莱顿   |"<<endl;
    cout<<"|   5.水晶宫  6.曼城队   |"<<endl;
    cout<<"|   7.曼联队  0.退出了   |"<<endl;
    cout<<"-------------------------"<<endl;
}

void CtrlProcess(vector<channel>& channels)
{
    int cnt = 0;

    menu();
    cout<<"请选择:";
    while(1)
    {
        // 选择任务
        int select = 0;
        string tmp;
        getline(cin,tmp);
        select = atoi(tmp.c_str());

        if(select >= 1 && select <=7)
        {
            int cmdnum = select-1;// 任务码

            // 发送任务
            write(channels[cnt]._cmdfd,&cmdnum,sizeof(cmdnum));

            // 进程选择(轮转)
            cnt++;
            cnt %= channels.size();
        }
        else if(select == 0)break;
        else cout<<"重新选择"<<endl;
    }
}

void QuitProcess(vector<channel>& channels)
{
    // Bug:子进程继承了父进程的写端，故在创建子进程时，越晚创建的子进程继承父进程的写端会越多
    //     最后一个子进程将继承所有父进程的所有写端
    //     如果现在是以关闭一个写端等待一个子进程且是按照子进程优先被创建的顺序，则整个进程会被阻塞
    // for(auto& t:channels)
    // {
    //     close(t._cmdfd);
    //     waitpid(t._processid,nullptr,0);
    // }


    // 方法一: 用一个循环，优先从晚被创建的子进程开始关闭和回收
        // int end = channels.size() - 1;
        // for(int i = end;i >= 0;i--)
        // {
        //     close(channels[i]._cmdfd);
        //     waitpid(channels[i]._processid,nullptr,0);
        // }

    // 方法二：用两次循环，先关闭所有父进程的写端
    //        
    // 关闭所有打开的文件描述符
    // for(auto& t:channels)
    // {
    //     close(t._cmdfd);
    // }

    // // 等待所有的子进程
    // for(auto& t:channels)
    // {
    //     waitpid(t._processid,nullptr,0);
    // }

    // 方法三：创建子进程时将继承父进程的历史写端关闭
    // 这里一个循环正常关闭就行
    for(auto& t:channels)
    {
        close(t._cmdfd);
        waitpid(t._processid,nullptr,0);
    }
}

// void Debug2(vector<channel>& channels)
// {
//     int id = (pid_t)channels[0]._processid;
//     cout<<"fathe "<<id<<endl;
//     write(channels[0]._cmdfd,&id,sizeof(int));
//     sleep(5000);
// }

int main()
{
    LoadTask();//加载可选择的任务
    vector<channel> channels;// 组织channel
    // 1.初始化
    InitProcessPool(channels);
    //Debug1();

    // 2. 控制进程
    CtrlProcess(channels);

    //Debug2(channels);

    // 3. 清理退出
    QuitProcess(channels);

    return 0;
}











